package com.op.parcel.manage.config;

import com.op.parcel.boot.config.IgnoredUrlsConfig;
import com.op.parcel.manage.config.security.UserDetailsServiceImpl;
import com.op.parcel.manage.config.security.authentication.DeniedHandler;
import com.op.parcel.manage.config.security.authentication.EntryPoint;
import com.op.parcel.manage.config.security.authentication.FailureHandler;
import com.op.parcel.manage.config.security.authentication.SuccessHandler;
import com.op.parcel.manage.config.security.jwt.JWTAuthenticationFilter;
import com.op.parcel.manage.config.security.permission.MyAccessDecisionManager;
import com.op.parcel.manage.config.security.permission.MySecurityMetadataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * Security 核心配置类
 * 开启控制权限至Controller
 *
 * @author xuan🐽
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private IgnoredUrlsConfig ignoredUrlsConfig;

	@Autowired
	private UserDetailsServiceImpl userDetailsService;

	@Autowired
	private SuccessHandler successHandler;

	@Autowired
	private FailureHandler failureHandler;

	@Autowired
	private DeniedHandler deniedHandler;

	@Autowired
	private EntryPoint entryPoint;

	// Custom JWT based security filter
	@Autowired
	private JWTAuthenticationFilter jwtAuthenticationFilter;

	@Autowired
	private MySecurityMetadataSource mySecurityMetadataSource;

	@Autowired
	private MyAccessDecisionManager myAccessDecisionManager;

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		http.cors();

		http
			// we don't need CSRF because our token is invulnerable
			.csrf().disable()
			//don't create session
			.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
			//iframe sameOrigin
			.headers().frameOptions().sameOrigin();

		//JWT过滤器
		http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
			.authorizeRequests()
			//权限决断器
			.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
				@Override
				public <O extends FilterSecurityInterceptor> O postProcess(O o) {
					o.setSecurityMetadataSource(mySecurityMetadataSource);
					o.setAccessDecisionManager(myAccessDecisionManager);
					return o;
				}
			}).anyRequest().authenticated()
			.and()
			//form login
			.formLogin()
			.loginProcessingUrl("/login").permitAll()
			.successHandler(successHandler)
			.failureHandler(failureHandler).and()
			.exceptionHandling().accessDeniedHandler(deniedHandler).and()
			.exceptionHandling().authenticationEntryPoint(entryPoint).and()
			.logout().permitAll();

	}

	@Override
	public void configure(WebSecurity web) {
		// AuthenticationTokenFilter will ignore the below paths
		WebSecurity.IgnoredRequestConfigurer ignoring = web.ignoring();
		for (String url : ignoredUrlsConfig.getUrls()) {
			ignoring.antMatchers(url);
		}
	}

}
